package com.xlongwei.info.handler;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.networknt.config.Config;
import com.networknt.config.JsonMapper;
import com.networknt.httpstring.AttachmentConstants;
import com.networknt.httpstring.ContentType;
import com.networknt.utility.Constants;
import com.networknt.utility.StringUtils;
import com.networknt.utility.Util;

import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.form.FormData;
import io.undertow.server.handlers.form.FormData.FormValue;
import io.undertow.server.handlers.form.FormDataParser;
import io.undertow.server.handlers.form.FormParserFactory;
import io.undertow.server.handlers.form.FormParserFactory.Builder;
import io.undertow.util.Headers;
import lombok.extern.slf4j.Slf4j;

/**
 * 处理参数、正文、表单为Map，可以配置为middleware，也可以直接调parseBody
 */
@Slf4j
public class MyBodyHandler extends MyMiddlewareHandler {

	@Override
	public void handleRequest(HttpServerExchange exchange) throws Exception {
		parseBody(exchange);
		super.handleRequest(exchange);
	}

	public static void parseBody(HttpServerExchange exchange) {
		Map<String, Object> body = new HashMap<>(4);
		Map<String, Deque<String>> params = exchange.getQueryParameters();
		for (Entry<String, Deque<String>> entry : params.entrySet()) {
			String param = entry.getKey();
			Deque<String> deque = entry.getValue();
			if (deque.size() > 1) {
				body.put(param, new ArrayList<>(deque));
			} else {
				body.put(param, deque.getFirst());
			}
		}
		String contentType = StringUtils.trimToEmpty(exchange.getRequestHeaders().getFirst(Headers.CONTENT_TYPE));
		int contentLength = Util.parseInteger(exchange.getRequestHeaders().getFirst(Headers.CONTENT_LENGTH));
		try { if(contentLength > 0) {
			exchange.startBlocking();// 参考BodyHandler
			boolean isForm = (contentType.startsWith(ContentType.MULTIPART_FORM_DATA_VALUE.value())
					|| contentType.startsWith(ContentType.APPLICATION_FORM_URLENCODED_VALUE.value()));
			if (isForm) {
				Builder builder = FormParserFactory.builder();
				builder.setDefaultCharset(Constants.DEFAULT_CHARACTER);
				FormParserFactory formParserFactory = builder.build();
				FormDataParser parser = formParserFactory.createParser(exchange);
				if (parser != null) {
					FormData formData = parser.parseBlocking();
					for (String name : formData) {
						Deque<FormValue> deque = formData.get(name);
						if (deque.size() > 1) {
							List<Object> list = new ArrayList<>();
							for (FormValue formValue : deque) {
								list.add(formValue.isFileItem() ? formValue : formValue.getValue());
							}
							body.put(name, list);
						} else {
							FormValue formValue = deque.getFirst();
							body.put(name, formValue.isFileItem() ? formValue : formValue.getValue());
						}
					}
				}
			}
				if(!isForm || exchange.getAttachment(FormDataParser.FORM_DATA) == null) {
					InputStream inputStream = exchange.getInputStream();
					String string = StringUtils.inputStreamToString(inputStream, StandardCharsets.UTF_8);
					body.putAll(stringBodyToMap(exchange, string));
				}
			}
		} catch (Exception e) {
			log.info("fail to parse body: {} {}:{}", e.getMessage(), contentType, contentLength);
		}
		if (log.isInfoEnabled() && !body.isEmpty()) {
			log.info("{}", JsonMapper.toJson(body));
		}
		exchange.putAttachment(AttachmentConstants.REQUEST_BODY, body);
	}

	/**
	 * @param string a=1&a=2
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, Object> stringBodyToMap(HttpServerExchange exchange, String string) {
		if (StringUtils.isNotBlank(string = StringUtils.trimToEmpty(string))) {
			try {
				if ('{' == string.charAt(0)) {
					return JsonMapper.string2Map(string);
				} else if (string.contains("=")) {
					String[] params = StringUtils.split(string, '&');
					Map<String, Object> map = new HashMap<>(params.length);
					for (String pair : params) {
						String[] nv = StringUtils.split(pair, '=');
						if (nv != null && nv.length == 2) {
							String name = nv[0], value = Util.urlDecode(nv[1]);
							if (StringUtils.isBlank(name) || StringUtils.isBlank(value)) {
								continue;
							}
							Object obj = map.get(name);
							if (obj == null) {
								map.put(name, value);
							} else if (obj instanceof String) {
								map.put(name, new ArrayList<>(Arrays.asList((String) obj, value)));
							} else {
								((List<String>) obj).add(value);
							}
						}
					}
					return map;
				}
			} catch (Exception e) {
			} finally {
				exchange.putAttachment(AttachmentConstants.REQUEST_BODY_STRING, string);
			}
		}
		return Collections.emptyMap();
	}

	/** 获取请求参数 */
	public static String getParam(HttpServerExchange exchange, String name) {
		return getObject(exchange, name, String.class);
	}

	/** FormValue包含fileName+FileItem */
	public static FormValue getFile(HttpServerExchange exchange, String name) {
		return getObject(exchange, name, FormValue.class);
	}

	/** 获取正文字符串 */
	public static String getBodyString(HttpServerExchange exchange) {
		return exchange.getAttachment(AttachmentConstants.REQUEST_BODY_STRING);
	}

	/** 获取请求参数名集合 */
	public static Set<String> getParamNames(HttpServerExchange exchange) {
		Map<String, Object> body = getBody(exchange);
		return body != null ? body.keySet() : Collections.emptySet();
	}

	/**
	 * @param name  参数名
	 * @param clazz 支持String、FormValue、List
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <T> T getObject(HttpServerExchange exchange, String name, Class<T> clazz) {
		Map<String, Object> body = getBody(exchange);
		if (body != null) {
			Object obj = body.get(name);
			if (obj != null) {
				Class<? extends Object> clz = obj.getClass();
				if (clazz == clz || clazz.isAssignableFrom(clz)) {
					return (T) obj;
				} else if (List.class.isAssignableFrom(clz)) {
					obj = ((List) obj).get(0);
					clz = obj.getClass();
					if (clazz == clz || clazz.isAssignableFrom(clz)) {
						return (T) obj;
					}
				} else if (String.class == clazz) {
					return (T) obj.toString();
				}
			}
		}
		return null;
	}

	@SuppressWarnings({ "unchecked", })
	public static Map<String, Object> getBody(HttpServerExchange exchange) {
		return (Map<String, Object>) exchange.getAttachment(AttachmentConstants.REQUEST_BODY);
	}

	static {
		SimpleModule simpleModule = new SimpleModule();
		simpleModule.addSerializer(FormValue.class, new FormValueSerializer());
		JsonMapper.objectMapper.registerModule(simpleModule);
		Config.getInstance().getMapper().registerModule(simpleModule);
	}

	@SuppressWarnings("serial")
	public static class FormValueSerializer extends StdSerializer<FormValue> {
		protected FormValueSerializer() {
			super(FormValue.class);
		}

		@Override
		public void serialize(FormValue value, JsonGenerator gen, SerializerProvider provider) throws IOException {
			if (value.isFileItem()) {
				gen.writeStartObject();
				gen.writeStringField("fileName", value.getFileName());
				gen.writeNumberField("fileSize", value.getFileItem().getFileSize());
				gen.writeEndObject();
			} else {
				gen.writeString(value.getValue());
			}
		}
	}
}
